﻿using Framework.SqlSugarCore.Abstractions;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Volo.Abp;
using Volo.Abp.Data;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Threading;
using Volo.Abp.Uow;

namespace Framework.SqlSugarCore.Uow
{
	/// <summary>
	/// 工作单元数据库上下文提供者
	/// </summary>
	/// <typeparam name="TDbContext"></typeparam>
	public class UnitOfWorkSqlsugarDbContextProvider<TDbContext> : ISugarDbContextProvider<TDbContext> where TDbContext : ISqlSugarDbContext
	{
		public ILogger<UnitOfWorkSqlsugarDbContextProvider<TDbContext>> Logger { get; set; }
		public IServiceProvider ServiceProvider { get; set; }

		protected readonly IUnitOfWorkManager UnitOfWorkManager;
		protected readonly IConnectionStringResolver ConnectionStringResolver;
		protected readonly ICancellationTokenProvider CancellationTokenProvider;
		protected readonly ICurrentTenant CurrentTenant;

		public UnitOfWorkSqlsugarDbContextProvider(
			IUnitOfWorkManager unitOfWorkManager,
			IConnectionStringResolver connectionStringResolver,
			ICancellationTokenProvider cancellationTokenProvider,
			ICurrentTenant currentTenant
		)
		{
			UnitOfWorkManager = unitOfWorkManager;
			ConnectionStringResolver = connectionStringResolver;
			CancellationTokenProvider = cancellationTokenProvider;
			CurrentTenant = currentTenant;
			Logger = NullLogger<UnitOfWorkSqlsugarDbContextProvider<TDbContext>>.Instance;
		}

		public virtual async Task<TDbContext> GetDbContextAsync()
		{

			var connectionStringName = ConnectionStrings.DefaultConnectionStringName;

			//获取当前连接字符串，未多租户时，默认为空
			var connectionString = await ResolveConnectionStringAsync(connectionStringName);
			var dbContextKey = $"{this.GetType().FullName}_{connectionString}";

			var unitOfWork = UnitOfWorkManager.Current;
			if (unitOfWork == null)
			{
				throw new AbpException("DbContext 只能在工作单元内工作，当前DbContext没有工作单元，如需创建新线程并发操作，请手动创建工作单元");
			}

			//尝试当前工作单元获取db
			var databaseApi = unitOfWork.FindDatabaseApi(dbContextKey);

			//当前没有db创建一个新的db
			if (databaseApi == null)
			{
				//db根据连接字符串来创建
				databaseApi = new SqlSugarDatabaseApi(
					await CreateDbContextAsync(unitOfWork, connectionStringName, connectionString)
				);

				//创建的db加入到当前工作单元中
				unitOfWork.AddDatabaseApi(dbContextKey, databaseApi);

			}
			return (TDbContext)((SqlSugarDatabaseApi)databaseApi).DbContext;
		}

		/// <summary>
		/// db根据连接字符串来创建
		/// </summary>
		/// <param name="unitOfWork">工作单元</param>
		/// <param name="connectionStringName">数据库连接字符串名称</param>
		/// <param name="connectionString">数据库连接字符</param>
		/// <returns></returns>
		protected virtual async Task<TDbContext> CreateDbContextAsync(IUnitOfWork unitOfWork, string connectionStringName, string connectionString)
		{
			var creationContext = new SqlSugarDbContextCreationContext(connectionStringName, connectionString);
			//将连接key进行传值
			using (SqlSugarDbContextCreationContext.Use(creationContext))
			{
				var dbContext = await CreateDbContextAsync(unitOfWork);
				return dbContext;
			}
		}

		/// <summary>
		/// 创建连接
		/// </summary>
		/// <param name="unitOfWork">工作单元</param>
		/// <returns></returns>
		protected virtual async Task<TDbContext> CreateDbContextAsync(IUnitOfWork unitOfWork)
		{
			return unitOfWork.Options.IsTransactional
				? await CreateDbContextWithTransactionAsync(unitOfWork)
				: unitOfWork.ServiceProvider.GetRequiredService<TDbContext>();
		}

		/// <summary>
		/// 根据事务创建连接
		/// </summary>
		/// <param name="unitOfWork">工作单元</param>
		/// <returns></returns>
		protected virtual async Task<TDbContext> CreateDbContextWithTransactionAsync(IUnitOfWork unitOfWork)
		{
			//事务key
			var transactionApiKey = $"SqlsugarCore_{SqlSugarDbContextCreationContext.Current.ConnectionString}";

			//尝试查找事务
			var activeTransaction = unitOfWork.FindTransactionApi(transactionApiKey) as SqlSugarTransactionApi;

			//该db还没有进行开启事务
			if (activeTransaction == null)
			{
				//获取到db添加事务即可
				var dbContext = unitOfWork.ServiceProvider.GetRequiredService<TDbContext>();
				var transaction = new SqlSugarTransactionApi(
						  dbContext
					  );
				unitOfWork.AddTransactionApi(transactionApiKey, transaction);

				await dbContext.SqlSugarClient.Ado.BeginTranAsync();
				return dbContext;
			}
			else
			{
				return (TDbContext)activeTransaction.GetDbContext();
			}
		}

		/// <summary>
		/// 获取当前连接字符串，未多租户时，默认为空
		/// </summary>
		/// <param name="connectionStringName"></param>
		/// <returns></returns>
		protected virtual async Task<string> ResolveConnectionStringAsync(string connectionStringName)
		{
			if (typeof(TDbContext).IsDefined(typeof(IgnoreMultiTenancyAttribute), false))
			{
				using (CurrentTenant.Change(null))
				{
					return await ConnectionStringResolver.ResolveAsync(connectionStringName);
				}
			}

			return await ConnectionStringResolver.ResolveAsync(connectionStringName);
		}

	}
}
